<%@ page language="java" import="up6.database.DBFile" pageEncoding="UTF-8"%>
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ page import="up6.XDebug" %>
<%@ page import="up6.*" %>
<%@ page import="up6.model.*" %>
<%@ page import="up6.biz.*" %>
<%@ page import="up6.store.*" %>
<%@ page import="up6.database.*" %>
<%@ page import="up6.store.FileBlockWriter" %>
<%@ page import="org.apache.commons.fileupload.FileItem" %>
<%@ page import="org.apache.commons.fileupload.FileItemFactory" %>
<%@ page import="org.apache.commons.fileupload.FileUploadException" %>
<%@ page import="org.apache.commons.fileupload.disk.DiskFileItemFactory" %>
<%@ page import="org.apache.commons.fileupload.servlet.ServletFileUpload" %>
<%@ page import="org.apache.commons.lang.*" %>
<%@ page import="java.net.URLDecoder"%>
<%@ page import="java.util.Iterator"%>
<%@ page import="net.sf.json.JSONObject"%>
<%@ page import="java.util.List"%>
<%@ page import="java.io.InputStream"%>
<%@ page import="java.io.ByteArrayOutputStream"%>
<%
out.clear();
/*
	此页面负责将文件块数据写入文件中。
	此页面一般由控件负责调用
	参数：
		uid
		idSvr
		md5
		lenSvr
		pathSvr
		RangePos
		fd_idSvr
		fd_lenSvr
	更新记录：
		2022-05-30 完善Minio块合并逻辑
		2017-10-23 增加删除文件块缓存操作
		2017-07-13 取消数据库操作
		2016-04-09 优化文件存储逻辑，增加更新文件夹进度逻辑
		2015-03-19 客户端提供pathSvr，此页面减少一次访问数据库的操作。
		2014-07-23 优化代码。
		2012-10-25 整合更新文件进度信息功能。减少客户端的AJAX调用。
		2012-04-18 取消更新文件上传进度信息逻辑。
		2012-04-12 更新文件大小变量类型，增加对2G以上文件的支持。
*/
//String path = request.getContextPath();
//String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

String uid 			= request.getHeader("uid");//
String id 			= request.getHeader("id");
String pid          = request.getHeader("pid");
String pidRoot      = request.getHeader("pidRoot");
String lenSvr		= request.getHeader("lenSvr");
String lenLoc		= request.getHeader("lenLoc");//本地文件大小
String blockOffset	= request.getHeader("blockOffset");
String blockSize	= request.getHeader("blockSize");
String blockIndex	= request.getHeader("blockIndex");//块索引，基于1
String blockCount   = request.getHeader("blockCount");//块总数
String blockMd5		= request.getHeader("blockMd5");
String complete		= request.getHeader("complete");
String fastdfs_id   = request.getHeader("fastdfs_id");//
String minio_id     = request.getHeader("minio_id");//
String pathSvr		= "";
String pathLoc		= "";
String pathRel		= "";//相对路径
String token		= request.getHeader("token");
boolean isLastBlock = StringUtils.equals(complete, "true");

//参数为空 
if(	 StringUtils.isEmpty( uid )
	|| StringUtils.isBlank( id )
	|| StringUtils.isEmpty( blockOffset ))
{
	XDebug.Output("param is null");
	return;
}
 
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List files = null;
try 
{
	files = upload.parseRequest(request);
} 
catch (FileUploadException e) 
{// 解析文件数据错误  
    out.println("read file data error:" + e.toString());
    return;
   
}

FileItem blockData = null;//文件块
FileItem thumbData = null;//缩略图，与最后一个文件块一起上传。
// 得到所有上传的文件
Iterator fileItr = files.iterator();
// 循环处理所有文件
while (fileItr.hasNext()) 
{
	// 得到当前文件
	FileItem rangeFile = (FileItem) fileItr.next();
	if(StringUtils.equals( rangeFile.getFieldName(),"pathSvr"))
	{
		pathSvr = rangeFile.getString();
		pathSvr = PathTool.url_decode(pathSvr);
	}
	else if(StringUtils.equals( rangeFile.getFieldName(),"pathRel"))
	{
		pathRel = rangeFile.getString();
		pathRel = PathTool.url_decode(pathRel);
	}
	else if(StringUtils.equals( rangeFile.getFieldName(),"thumb"))
	{
		thumbData = rangeFile;
	}
	else if(StringUtils.equals( rangeFile.getFieldName(),"file"))
	{
		blockData = rangeFile;
	}
}

boolean verify = false;
String msg = "";
String md5Svr = "";
ByteArrayOutputStream ostm = StreamTool.toStream(blockData);
long blockSizeSvr = blockData.getSize();
pathLoc = new String(blockData.getName().getBytes(),"UTF-8");

FileInf fileSvr = new FileInf();
fileSvr.id = id;
fileSvr.pid = pid;
fileSvr.pidRoot = pidRoot;
fileSvr.fastdfs_id=fastdfs_id;
fileSvr.minio_id = minio_id;
fileSvr.lenSvr = Long.parseLong(lenSvr);
fileSvr.lenLoc = Long.parseLong(lenLoc);
fileSvr.sizeLoc = PathTool.BytesToString(fileSvr.lenLoc);
fileSvr.pathLoc = pathLoc;
fileSvr.pathSvr = pathSvr;
fileSvr.pathRel = pathRel;
fileSvr.perSvr="100%";
fileSvr.complete=true;
fileSvr.blockIndex = Integer.parseInt(blockIndex);
fileSvr.blockOffset = Long.parseLong(blockOffset);
fileSvr.blockSize = Integer.parseInt(blockSize);//加密后的块大小
fileSvr.blockCount = Integer.parseInt(blockCount);
fileSvr.nameLoc = PathTool.getName(pathLoc);
fileSvr.nameSvr = fileSvr.nameLoc;
fileSvr.encrypt=ConfigReader.storageEncrypt();//存储加密

//传输加密
if (ConfigReader.postEncrypt())
{
	CryptoTool ct   = new CryptoTool();
    pathSvr = ct.decrypt(pathSvr,"UTF-8");
    fileSvr.pathSvr = pathSvr;
    //加密存储？
    if(!fileSvr.encrypt) ostm = ct.decrypt(ostm);
    blockSizeSvr = ostm.size();
}

//token验证
WebSafe ws = new WebSafe();
boolean ret = ws.validToken(token,fileSvr,"block");
//token验证失败
verify = ret;
if(!verify)
{
	response.setStatus(500);
	msg = String.format("token error loc:%s",token);
}

//验证文件块MD5
if(!StringUtils.isBlank(blockMd5)) md5Svr = Md5Tool.fileToMD5(ostm);

//验证块大小
if(verify) verify = Integer.parseInt(blockSize) == ostm.size();

if(!verify)
{
	response.setStatus(500);
	msg = "block size error sizeSvr:" + blockSizeSvr + "sizeLoc:" + blockSize;
}

if(verify && !StringUtils.isBlank(blockMd5))
{
	verify = md5Svr.equals(blockMd5);
	if(!verify)
	{
		msg="block md5 error";
		out.write(msg);
		response.setStatus(500);
		return;
	}
}

if(verify)
{
	PathBuilder pb = new PathBuilder();
	pathSvr = pb.relToAbs(pathSvr);
	fileSvr.pathSvr = pathSvr;
	
	//块存储器
	FileBlockWriter fw = ConfigReader.blockWriter();
	boolean needGenId = !StringUtils.isEmpty(pid);//仅子文件创建 
	if(needGenId) needGenId = 1 == fileSvr.blockIndex;
	if(needGenId&&fw.storage == StorageType.FastDFS) needGenId = StringUtils.isEmpty(fastdfs_id);
	if(needGenId&&fw.storage == StorageType.Minio) needGenId = StringUtils.isEmpty(minio_id);
	//子文件.仅第一块生成存储.id
	if( needGenId ) 
	{
		String newID  = fw.make(fileSvr);
		
		if(newID.isEmpty() && StorageType.IO == fw.storage)
		{
			out.write("io make error");
			response.setStatus(500);
			return;
		}
		
		if(StorageType.FastDFS== fw.storage)
		{
			fileSvr.fastdfs_id = newID;
			if(newID.isEmpty())
			{
				out.write("FastDFS init error");
				response.setStatus(500);
				return;
			}
		}
		
		if(StorageType.Minio== fw.storage) {
			fileSvr.minio_id = newID;
			if(StringUtils.isEmpty((newID)))
			{
				msg="Minio CreateMultipartUpload error";
				out.write(msg);
				response.setStatus(500);
				return;
			}
		}
	}
	
	if(!StringUtils.isEmpty(fileSvr.pidRoot)) fileSvr.saveScheme();//保存到层级信息
	
	fileSvr.etag = fw.write(fileSvr,ostm);//保存文件块数据
	
	if(StorageType.IO == fw.storage && fileSvr.etag.isEmpty())
	{
		out.write("io write block error");
		response.setStatus(500);
		return;
	}
	
	//FastDFS error
	if(fw.storage==StorageType.FastDFS&& fileSvr.etag.isEmpty())
	{
		out.write("FastDFS write block error");
		response.setStatus(500);
		return;		
	}
	
	//minio upload part 错误
	if(StorageType.Minio==fw.storage&&fileSvr.etag.isEmpty())
	{
		msg="Minio write block data error";
		out.write(msg);
		response.setStatus(500);
		return;
	}
	
	if(StorageType.Minio == fw.storage)
	{
		fileSvr.saveEtags();//保存块etag信息
		//最后一块=>合并块
		if(isLastBlock)
			if(!MinioTool.CompleteMultipartUpload(fileSvr))
			{
				out.clear();
				out.write("minio CompleteMultipartUpload error");
				response.setStatus(500);
				return;
			}
	}
	
	up6_biz_event.file_post_block(id,Integer.parseInt(blockIndex));
	
	//保存缩略图，最后一块
	if( isLastBlock && thumbData != null)
	{
		String thumbPath = pathSvr+".thumb.png";
		//res.CreateFile(thumbPath, thumbData.getSize());
		//res.write(0, thumbPath, thumbData);
	}
	
	JSONObject o = new JSONObject();
	o.put("msg", "ok");
	o.put("md5", md5Svr);	
	o.put("offset", blockOffset);
	JSONObject fds = new JSONObject();
    fds.put("fastdfs_id",fastdfs_id);//将FastDFS.id回传给控件
    fds.put("minio_id",fileSvr.minio_id);//将UploadId回传给控件
    if(1==fileSvr.blockIndex) o.put("fields",fds);
	msg = o.toString();
}
blockData.delete();
out.write(msg);
%>